#include "http_server.h"
#include "http_request.h"

#include "esp_log.h"
#define TAG "httpd.tools"
#define log_e(...) ESP_LOGE(TAG, __VA_ARGS__)
#define log_i(...) ESP_LOGI(TAG, __VA_ARGS__)
#define log_w(...) ESP_LOGW(TAG, __VA_ARGS__)

/* 👇👇👇👇👇👇👇👇功能函数区👇👇👇👇👇👇👇👇 */
const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
{
    const size_t base_pathlen = strlen(base_path);
    size_t pathlen = strlen(uri);
    const char *quest = strchr(uri, '?');
    if (quest)
    {
        pathlen = MIN(pathlen, quest - uri);
    }
    const char *hash = strchr(uri, '#');
    if (hash)
    {
        pathlen = MIN(pathlen, hash - uri);
    }
    if (base_pathlen + pathlen + 1 > destsize)
    {
        /* Full path string won't fit into destination buffer */
        return NULL;
    }
    /* Construct full path (base + path) */
    strcpy(dest, base_path);
    strlcpy(dest + base_pathlen, uri, pathlen + 1);
    /* Return pointer to path, skipping the base */
    return dest + base_pathlen;
}
/**
 *  发送文件
 */
esp_err_t httpd_send_file(httpd_req_t *req, char *path)
{
    if (!req)
        return ESP_FAIL;
    http_server_t server = httpd_get_global_user_ctx(req->handle);
    if (!server)
        return ESP_FAIL;
    FILE *fd = fopen(path, "r");
    if (fd)
    {
        fseek(fd, 0, SEEK_END);
        uint32_t file_size = ftell(fd);
        fseek(fd, 0, SEEK_SET);
        char file_size_str[15] = {0};
        sprintf(file_size_str, "%ld", file_size);
        httpd_resp_set_hdr(req, "Content-Length", file_size_str);
        if (strstr(path, ".gz"))
            httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
        uint32_t read_size;
        uint16_t buf_size = sizeof(server->data);
        if (buf_size > 4096)
            buf_size = 4096;
        do
        {
            buf_size = MIN(buf_size, file_size);
            read_size = fread(server->data, 1, buf_size, fd);
            if (read_size)
            {
                file_size -= read_size;
                esp_err_t err = httpd_resp_send_chunk(req, (const char *)server->data, read_size);
                log_i("send %ld data, Remaining size %lu, err %d", read_size, file_size, err);
            }
        } while (file_size);
        fclose(fd);
        httpd_resp_send_chunk(req, NULL, 0);
    }
    else
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Failed to open file.");
    return ESP_OK;
}
/**
 *      Get req ip
 */
char *http_server_get_req_ip(httpd_req_t *req)
{
    if (!req)
        return 0;
    int sockfd = httpd_req_to_sockfd(req);
    if (sockfd == -1)
        return 0;
    static char ipstr[16];
    memset(ipstr, 0, sizeof(ipstr));
#ifndef CONFIG_LWIP_IPV6
    struct sockaddr_in addr; // esp_http_server uses IPv4 addressing
#else
    struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
#endif
    socklen_t addr_size = sizeof(addr);
    if (!getpeername(sockfd, (struct sockaddr *)&addr, &addr_size))
    {
#ifndef CONFIG_LWIP_IPV6
        /* IPV4 */
        inet_ntop(AF_INET, &addr.sin_addr, ipstr, sizeof(ipstr));
        /* port string */
        // ntohs(addr.sin_port);
#else
        /* IPV6 */
        inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
        /* port string */
        // ntohs(addr.sin6_port);
#endif
        return ipstr;
    }
    return 0;
}

/**
 *      检测cjson对象中是否存在 指定字段
 *      name    : 指定字段
 *      存在返回 1
 *      不存在返回 0 , 并且发送错误数据到客户端
 */
uint8_t json_object_has_item(httpd_req_t *req, cJSON *json, char *name)
{
    if (!json || !name)
        return 0;
    if (cJSON_HasObjectItem(json, name))
        return 1;
    if (req)
        req_send_err("The data field does not exist");
    return 0;
}
/**
 *      获取json 字段的字符串数据
 *      成功返回 指针
 *      失败返回 0 并且发送错误数据到客户端
 */
char *json_get_string(httpd_req_t *req, cJSON *json, char *item)
{
    char *str = cJSON_GetStringValue(cJSON_GetObjectItem(json, item));
    if (!str)
        req_send_err("Data must be a string");
    return str;
}

/**
 *      将请求中携带的内容, 转换成 json 对象
 *      req     : http server 获取到的请求指针
 *      成功返回 json对象指针
 *      失败返回 0
 */
cJSON *http_server_data_parse_to_json(httpd_req_t *req, uint8_t is_obj)
{
    if (!req)
        return 0;
    http_server_t server = httpd_get_global_user_ctx(req->handle);
    int remaining = req->content_len;
    if (!remaining || remaining > 4096)
    {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "No parameterse");
        return 0;
    }
    if (remaining + 1 < 4096)
        memset(server->data, 0, remaining + 1);
    else
        memset(server->data, 0, 4096);
    log_i("Remaining size : %d", remaining);
    /* Receive the file part by part into a buffer */
    int received = 0;
    if ((received = httpd_req_recv(req, (char *)server->data, MIN(remaining, 4096))) <= 0)
    {
        if (received == HTTPD_SOCK_ERR_TIMEOUT)
        {
            /* Retry if timeout occurred */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to recv buffer, timeout");
            return 0;
        }
        log_e("Reception failed!");
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive data");
        return 0;
    }
    printf("recv : %s\n", server->data);
    cJSON *json = cJSON_Parse((char *)server->data);
    if (!json)
    {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to Parse json data");
        return 0;
    }
    if (is_obj && !cJSON_IsObject(json))
    {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "The data is not a JSON obj object");
        cJSON_Delete(json);
        return 0;
    }
    return json;
}
/* 👆👆👆👆👆👆👆功能函数区👆👆👆👆👆👆👆 */